Skip to content

fix: make get_queue_info reader-callable#267

Merged
NikolayS merged 2 commits into
mainfrom
fix/get-queue-info-reader-perms-2
Jul 8, 2026
Merged

fix: make get_queue_info reader-callable#267
NikolayS merged 2 commits into
mainfrom
fix/get-queue-info-reader-perms-2

Conversation

@NikolayS

@NikolayS NikolayS commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Bug

pgque.get_queue_info() and pgque.get_queue_info(text) are granted to pgque_reader and documented in docs/reference.md / docs/monitoring.md as reader-usable monitoring functions, but a pgque_reader session fails at runtime:

ERROR:  permission denied for function seq_getval
CONTEXT:  PL/pgSQL function pgque.get_queue_info(text) line 58 at assignment
PL/pgSQL function pgque.get_queue_info() line 26 at FOR over SELECT rows

Root cause

get_queue_info is inherited from upstream PgQ as SECURITY INVOKER. The 1-arg overload internally calls pgque.seq_getval(text) to compute ev_new, and that helper's ACL is admin-only ({postgres, pgque_admin}). A reader therefore cannot execute it. The sibling functions get_consumer_info and get_batch_info are SECURITY DEFINER for exactly this reason and work fine for readers — get_queue_info was the odd one out.

Fix

Promote both get_queue_info overloads to SECURITY DEFINER with SET search_path = pgque, pg_catalog (mandatory for all SECURITY DEFINER per CLAUDE.md), mirroring the existing get_consumer_info / get_batch_info pattern. This grants no privilege beyond reading queue metadata plus the queue event sequence — the same surface the function already exposes to its (intended) readers.

  • Patch added to build/transform.sh (the source of truth for the generated installs), with a guard asserting exactly 2 overloads are promoted.
  • Regenerated sql/pgque.sql and sql/pgque-tle.sql so generated and source stay consistent. Rebuild is reproducible (re-running transform.sh yields no diff).

Audited the other reader-granted observability functions for the same trap: only get_queue_info calls seq_getval under SECURITY INVOKER. get_batch_info / get_consumer_info are already DEFINER; status() is admin-only by design and left as-is.

Test (red/green TDD)

New regression tests/test_get_queue_info_reader.sql (registered in tests/run_all.sql) creates a queue, ticks it, then under set role pgque_reader calls both overloads and asserts success + populated ev_new.

  • Against pre-fix SQL: fails with permission denied for function seq_getval (verified).
  • Against the fix: passes. Full tests/run_all.sql suite passes on PostgreSQL 18.

Manual verification

select pgque.create_queue('bug_q');
set role pgque_reader;
select count(*) from pgque.get_queue_info();        -- was: permission denied; now: succeeds
select queue_name, ev_new from pgque.get_queue_info('bug_q');
reset role;

Fixes #265

NikolayS and others added 2 commits June 2, 2026 14:43
Failing regression: a pgque_reader session calling
pgque.get_queue_info() / get_queue_info(text) hits
"permission denied for function seq_getval" because the
function is SECURITY INVOKER but calls the admin-only
pgque.seq_getval(text). Reproduces issue #265.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
get_queue_info(text) calls the admin-only pgque.seq_getval(text)
but ships as SECURITY INVOKER, so a pgque_reader session -- the
exact role it is granted to and documented for -- fails at
runtime with "permission denied for function seq_getval".

Promote both get_queue_info overloads to SECURITY DEFINER with
SET search_path = pgque, pg_catalog (mandatory per CLAUDE.md),
mirroring the sibling get_consumer_info / get_batch_info. Patch
added to build/transform.sh and the generated sql/pgque.sql and
sql/pgque-tle.sql regenerated so source and generated stay in
sync. Grants no privilege beyond reading queue metadata.

Fixes #265

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

NikolayS commented Jun 7, 2026

Copy link
Copy Markdown
Owner Author

REV Code Review Report

Status: PASS

Scope reviewed: #267 at 7fcda8ee95bec7a3de5f8142e1dc96afaba55bbc.

Checks:

  • Diff is narrow: get_queue_info() / get_queue_info(text) privilege fix plus one regression test.
  • SECURITY DEFINER is paired with SET search_path = pgque, pg_catalog in generated install SQL.
  • build/transform.sh is the source-of-truth patch point and has a guard requiring exactly two overloads to be promoted.
  • Regression covers the actual granted role (pgque_reader) and both overloads after exercising the seq_getval() path.
  • CI is green for the PR’s full matrix.

No blockers found. This is ready from REV/code-review perspective.

@NikolayS

NikolayS commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

samorev Code Review Report

Pipeline Coverage
PASS Not reported

No issues found. Reviewed for security, bugs, tests, guidelines, and documentation.

Result: PASSED


Summary

Area Findings Potential Filtered
CI/Pipeline 0 0 0
Security 0 0 0
Bugs 0 0 0
Tests 0 0 0
Guidelines 0 0 0
Docs 0 0 0
Metadata 0 0 0

Note:

  • Findings: High-confidence issues (8-10/10) - blocking or non-blocking per severity
  • Potential: Medium-confidence issues (4-7/10) - review manually
  • Filtered: Low-confidence issues (0-3/10) - excluded as likely false positives
Review metadata
provider=github
kind=pr
project=NikolayS/PgQue
number=267
target=github:NikolayS/PgQue#267
state=OPEN
draft=false
diff_lines=157
diff_added=89
diff_removed=4
diff_bytes=5587
comments_count=1
commits_count=2
ci_status=success
ci_summary=total=14 success=14 failure=0 pending=0 other=0
prompt=.claude/commands/review-mr.md
blocking=false
posted_by=gh
no_comment=false
live_posting=posted

samorev-assisted review (AI analysis by Tanya301/samorev)

@NikolayS NikolayS merged commit 5881680 into main Jul 8, 2026
14 checks passed
@NikolayS

NikolayS commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

Post-merge code review — thorough pass

Reviewed the full diff against CLAUDE.md rules (SECURITY DEFINER contract, generated-file consistency, red/green TDD, SQL style) and verified the key claims empirically on PostgreSQL 18 in Docker.

Verdict: no blocking findings. The fix is correct, minimal, properly tested, and reproducible. Details below, including the security angle I checked hardest.

Correctness — clean

  • get_queue_info(text) calls pgque.seq_getval(_queue_event_seq); _queue_event_seq is read from the pgque.queue.queue_event_seq column, not from a caller argument. No injection surface via the function's text parameter.
  • Both overloads land as SECURITY DEFINER with SET search_path = pgque, pg_catalog — verified in the live catalog (prosecdef = t, proconfig = {search_path=pgque, pg_catalog}), satisfying the CLAUDE.md mandate.
  • No new information exposure: the zero-arg overload returning all-queue metadata was already reader-granted and intended for readers; DEFINER only fixes the seq_getval execute failure, it doesn't widen the row set.

Security — PUBLIC-execute trap checked and NOT present

The one real risk when flipping a function to SECURITY DEFINER is Postgres's default PUBLIC EXECUTE grant: DEFINER + PUBLIC would let any role run it with owner privileges. Verified this is handled:

  • sql/pgque.sql:4824 does revoke execute on all functions in schema pgque from public.
  • Live ACL on both overloads: {postgres=X/postgres, pgque_reader=X/postgres, pgque_admin=X/postgres} — PUBLIC absent.
  • Empirical: a fresh unprivileged role (schema usage only, not in pgque_reader) calling pgque.get_queue_info() gets ERROR: permission denied for function get_queue_info. No leak.

Audit claim ("get_queue_info was the only one") — confirmed

Enumerated every seq_getval call site in the install:

  • get_queue_info(text) — the fix.
  • inside ticker() — writer/admin path, not a reader monitoring function.
  • inside seq_setval() — admin helper, not reader-granted.

Sibling reader-granted monitoring functions get_consumer_info / get_batch_info are already SECURITY DEFINER (verified). next_batch_info is SECURITY INVOKER and reader-granted but reads only tables the reader can already select, and calls no admin-only helper — no trap. The audit in the PR body holds.

Consistency — reproducible build

  • Checked out the merge commit, ran bash build/transform.sh fresh: empty diff. sql/pgque.sql and sql/pgque-tle.sql regenerate exactly as committed; both files carry the change.
  • The transform patch includes a fail-closed guard (defcount -ne 2exit 1), so if upstream PgQ ever restructures these overloads the build breaks loudly rather than silently under-patching. Good defensive touch.

Tests — genuine red/green

Ran tests/test_get_queue_info_reader.sql both ways on PG 18:

  • Red (pre-fix SQL, git show <parent>:sql/pgque.sql): fails with the exact ERROR: permission denied for function seq_getval / CONTEXT: ... get_queue_info(text) line 58.
  • Green (merged SQL): both overloads pass under set role pgque_reader, ev_new populated.
  • Registered in tests/run_all.sql; commit order is test-first then fix. TDD honored.

Nits (non-blocking, no action needed)

  • SECURITY DEFINER means these now run as the function owner (a superuser on a typical install). Safe here — static body, no dynamic SQL, pinned search_path — and it mirrors the existing get_consumer_info / get_batch_info pattern. Noted only for completeness.
  • Test-file header uses -- for a multi-line block; CLAUDE.md prefers /* */ for 2+ lines, but this matches the established convention in sibling tests (e.g. test_e2e_role_split.sql), so it's consistent with the repo, not a defect.

Reviewed by an independent post-merge pass (build reproduced + tests run red/green + ACLs checked on PostgreSQL 18).

@NikolayS

NikolayS commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

samorev Code Review Report

Pipeline Coverage
PASS Not reported

This report supersedes the earlier samorev review --fetch comment on this PR. That comment was a CI/draft gate check only and performed no code analysis (it hardcoded zeros in every area). The results below come from a genuine per-area review of the diff, with the key claims verified empirically on PostgreSQL 18.

No blocking issues. Reviewed for security, bugs, tests, guidelines, and documentation. Two candidates were examined and filtered as non-issues (see below).

Result: PASSED

What was verified

  • Security definer contract: both get_queue_info overloads land as security definer with set search_path = pgque, pg_catalog (live catalog: prosecdef = t, proconfig = {search_path=pgque, pg_catalog}).
  • No PUBLIC EXECUTE trap: sql/pgque.sql does revoke execute on all functions in schema pgque from public; live ACL on both overloads has no PUBLIC entry. An unprivileged role (schema usage only) calling pgque.get_queue_info() gets permission denied for function — no privilege leak from the definer flip.
  • No injection surface: the text parameter is a queue name; the sequence read is seq_getval(_queue_event_seq) where _queue_event_seq comes from the pgque.queue row, not the caller argument.
  • Audit ("only reader function affected") holds: every seq_getval call site enumerated; sibling reader functions get_consumer_info/get_batch_info are already security definer, next_batch_info reads only reader-visible tables.
  • Red/green test: tests/test_get_queue_info_reader.sql fails on the pre-fix SQL (permission denied for function seq_getval at get_queue_info(text)) and passes on the fix under set role pgque_reader; registered in tests/run_all.sql, committed test-first.
  • Generated-file consistency: bash build/transform.sh at the merge commit reproduces sql/pgque.sql and sql/pgque-tle.sql byte-identically (empty git diff).

Filtered (candidates examined, excluded as non-issues)

  • Securitysecurity definer means the functions run as the owner (typically superuser). Safe here: static body, no dynamic SQL, pinned search_path, PUBLIC revoked; mirrors the existing get_consumer_info/get_batch_info pattern. Not a defect.
  • Guidelines — the test file uses -- for a multi-line header where CLAUDE.md prefers /* ... */. Matches the established convention in sibling tests (e.g. test_e2e_role_split.sql), so it is consistent with the repo rather than a violation.

Summary

Area Findings Potential Filtered
CI/Pipeline 0 0 0
Security 0 0 1
Bugs 0 0 0
Tests 0 0 0
Guidelines 0 0 1
Docs 0 0 0
Metadata 0 0 0

Note:

  • Findings: High-confidence issues (8-10/10) - blocking or non-blocking per severity
  • Potential: Medium-confidence issues (4-7/10) - review manually
  • Filtered: Low-confidence issues (0-3/10) - excluded as likely false positives
Review metadata
provider=github
kind=pr
project=NikolayS/pgque
number=267
target=github:NikolayS/pgque#267
state=MERGED
draft=false
diff_lines=157
diff_added=89
diff_removed=4
diff_bytes=5587
comments_count=3
commits_count=2
ci_status=success
ci_summary=total=14 success=14 failure=0 pending=0 other=0
prompt=.claude/commands/review-mr.md
blocking=false
posted_by=gh
no_comment=false
live_posting=posted

samorev-assisted review (AI analysis by Tanya301/samorev)

@NikolayS NikolayS deleted the fix/get-queue-info-reader-perms-2 branch July 8, 2026 23:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs: evidence-backed functional verification of every documented command

1 participant